using AnimCollections;
using System;

namespace DA.AnimGraph {
    public unsafe static class AGPParser {
        public static byte[] ParseAGPToDry(PublishGraph pGraph) {

            // try once
            GraphBuffer buffer = new GraphBuffer(1024 * 10);
            //var head = buffer.Malloc<DryHead>();
            //*head = new DryHead();
            NodeLink* root = ParseNodes(pGraph.nodes, 0, ref buffer);

            if (buffer.blockCount > 1) {
                var newAllocSize = buffer.allDataSize + sizeof(IntPtr) * 4;
                // UnityEngine.Debug.Log($"[AGPParser] old, blockCount: {buffer.blockCount},  allDataSize: {buffer.allDataSize}");
                GraphBuffer newBuffer = new GraphBuffer(newAllocSize);

                //head = newBuffer.Malloc<DryHead>();
                //*head = new DryHead();
                root = ParseNodes(pGraph.nodes, 0, ref newBuffer);

                buffer.Dispose();
                buffer = newBuffer;
            }

            // 从root抽取：
            //var size = buffer.allDataSize;
            //head->size = buffer.allDataSize;
            var (addrStart, addrEnd) = buffer.GetBlockDataAddrRange();
            var dryParam = new DryParam() {
                baseAddr = addrStart,
                bufferStartAddr = addrStart,
                bufferEndAddr = addrEnd,
            };
            root->Dry(dryParam);

            if (buffer.blockCount != 1) {
                throw new Exception($"[AGPParser] 导出异常，buffer.blockCount = {buffer.blockCount}");
            }
            // UnityEngine.Debug.Log($"[AGPParser] new, blockCount: {buffer.blockCount},  allDataSize: {buffer.allDataSize}");
            var dryData = buffer.Dump(); //用于写入bin，Dump之前把值改完

            buffer.Dispose();
            return dryData.ToArray();
        }

        /// <summary>
        /// 从AGD解析出AnimGraph的完整过程
        /// </summary>
        public static AnimGraph*[] ParseAGDToGraph(byte[] agd,
            out AnimGraphPublishAsset.Resource[] outResources,
            out ReadedTimeline[] outTimelines
        ) {
            AGDData dryData;
            using (var reader = new BinaryDataBufferReader(agd)) {
                dryData = reader.GetAGD();
            }

            AnimGraph*[] graphs = new AnimGraph*[dryData.graphs.Length];
            for (int i = 0; i < dryData.graphs.Length; i++) {
                var dryGraph = dryData.graphs[i].bytes;
                int size = dryGraph.Length + sizeof(AnimGraph);
                GraphBuffer buffer = new GraphBuffer(size);
                // dry整块写入buffer
                buffer.MallocBytes(dryGraph);
                // 从数据中拿到第一个node，并且执行wet
                var (addrStart, addrEnd) = buffer.GetBlockDataAddrRange();
                var dryParam = new DryParam() {
                    baseAddr = addrStart,
                    bufferStartAddr = addrStart,
                    bufferEndAddr = addrEnd,
                };
                var rootNode = (NodeLink*)addrStart;
                rootNode->Wet(dryParam);

                var graph = AnimGraph.New(ref buffer);
                graph->uuid = dryData.graphs[i].graphUuid;
                graph->name = dryData.graphs[i].graphName;
                graph->Init(rootNode);
                graphs[i] = graph;
            }

            outResources = dryData.resources;
            outTimelines = dryData.timelines;

            return graphs;
        }

        /// <summary>
        /// 从AGD解析出 dryData
        /// </summary>
        public static AGDData ParseAGDToDryData(byte[] agd) {
            AGDData dryData;
            using (var reader = new BinaryDataBufferReader(agd)) {
                dryData = reader.GetAGD();
            }
            return dryData;
        }

        /// <summary>
        /// 从 dryData 生成AG
        /// </summary>
        public static AnimGraph*[] ParseDryDataToGraph(AGDData.Graph[] dryGraphs) {
            AnimGraph*[] graphs = new AnimGraph*[dryGraphs.Length];
            for (int i = 0; i < dryGraphs.Length; i++) {
                var dryGraph = dryGraphs[i];
                int size = dryGraph.bytes.Length + sizeof(AnimGraph);
                GraphBuffer buffer = new GraphBuffer(size);
                // dry整块写入buffer
                buffer.MallocBytes(dryGraph.bytes);
                // 从数据中拿到第一个node，并且执行wet
                var (addrStart, addrEnd) = buffer.GetBlockDataAddrRange();
                var dryParam = new DryParam() {
                    baseAddr = addrStart,
                    bufferStartAddr = addrStart,
                    bufferEndAddr = addrEnd,
                };
                var rootNode = (NodeLink*)addrStart;
                rootNode->Wet(dryParam);

                var graph = AnimGraph.New(ref buffer);
                graph->uuid = dryGraphs[i].graphUuid;
                graph->name = dryGraphs[i].graphName;
                graph->Init(rootNode);
                graphs[i] = graph;
            }

            return graphs;
        }

        /// <summary>
        /// 从 dryData 生成AG，只生成指定的一个
        /// </summary>
        public static AnimGraph* ParseDryDataToGraph(AGDData.Graph[] dryGraphs, int index) {
            var dryGraph = dryGraphs[index];
            int size = dryGraph.bytes.Length + sizeof(AnimGraph);
            GraphBuffer buffer = new GraphBuffer(size);
            // dry整块写入buffer
            buffer.MallocBytes(dryGraph.bytes);
            // 从数据中拿到第一个node，并且执行wet
            var (addrStart, addrEnd) = buffer.GetBlockDataAddrRange();
            var dryParam = new DryParam() {
                baseAddr = addrStart,
                bufferStartAddr = addrStart,
                bufferEndAddr = addrEnd,
            };
            var rootNode = (NodeLink*)addrStart;
            rootNode->Wet(dryParam);

            var graph = AnimGraph.New(ref buffer);
            graph->uuid = dryGraphs[index].graphUuid;
            graph->name = dryGraphs[index].graphName;
            graph->Init(rootNode);

            return graph;
        }


        public static AnimGraph*[] ParseGraph(AnimGraphPublishAsset agp) {
            AnimGraph*[] graphs = new AnimGraph*[agp.graphs.Length];
            for (int i = 0; i < agp.graphs.Length; i++) {

                GraphBuffer buffer = new GraphBuffer(0);
                AnimGraph* graph = AnimGraph.New(ref buffer);

                PublishGraph pGraph = agp.graphs[i];
                NodeLink* root = ParseNodes(pGraph.nodes, 0, ref buffer);

                graph->uuid = pGraph.uuid;
                graph->name = pGraph.name;
                graph->Init(root);
                graphs[i] = graph;
            }

            return graphs;
        }


        internal static NodeLink* ParseNodes(PublishNode[] pNodes, int index, ref GraphBuffer buffer) {
            var pNode = pNodes[index];
            return pNode.Parse(pNodes, index, ref buffer);
        }

    }
}